<?php
namespace Model;

use Core\HHO, Dao\Rules as DRules, Exception;

class Rules {
	/**
	 * 初始状态。
	 * @var int
	 */
	const DAO_STATUS_INIT = 1;
	/**
	 * 等待提交预订。
	 * @var int
	 */
	const DAO_STATUS_LOCK = 2;
	/**
	 * 已成功预订。
	 * @var int
	 */
	const DAO_STATUS_SUCC = 3;
	/**
	 * 12306.cn 限制每单中的最大旅客数。
	 * @var int
	 */
	const PASSENGER_MAX = 5;

	private function __construct() {}

	public function import() {
		$rules = [];
		
		$data = is_file(HHO::$config['data.root'] . '/Rules.txt') ? file(HHO::$config['data.root'] . '/Rules.txt') : [];
		foreach ($data as $item) {
			$item = trim($item);
			if (!$item || $item[0] == '#') {
				continue;
			}
			
			$rule = preg_split('/\s+/', strtoupper(str_replace(['：', '，', '；'], [':', ',', ';'], $item)));
			
			// 预订的时间范围。
			$rule[3] = explode('-', $rule[3]);
			$rule[3][0] = strtotime($rule[2] . ' ' . $rule[3][0]);
			$rule[3][1] = strtotime($rule[2] . ' ' . $rule[3][1]);
			
			// 席位列表。
			$rule[4] = explode(',', trim($rule[4], " \t,"));
			foreach ($rule[4] as &$x) {
				$x = explode(':', $x);
				if (!in_array($x[1], Reserve::$seat)) {
					throw new Exception('席位“' . $x[1] . '”不支持');
				}
			}
			
			// 旅客列表。
			$rule[5] = explode(';', trim($rule[5], " \t;"));
			if (count($rule[5]) > self::PASSENGER_MAX) {
				throw new Exception("{$rule[0]}-{$rule[1]}（{$rule[2]}）的旅客数量超出" . self::PASSENGER_MAX . '个');
			}
			foreach ($rule[5] as &$x) {
				$x = explode(',', $x);
			}
			
			$rules[] = $rule;
		}
		
		// 删除所有用户已创建的会话。
		foreach (Reserve::$user as $uname => $password) {
			Reserve::getInstance($uname)->destruct();
		}
		
		DRules::getInstance()->truncate();
		
		$success = 0;
		
		foreach ($rules as $rule) {
			DRules::getInstance()->addRule($rule[0], $rule[1], $rule[2], $rule[3][0], $rule[3][1], $rule[4], $rule[5]) && $success++;
		}
		
		return $success;
	}

	public function passenger($train, $from, $to, $dater) {
		$x = strtotime(date('Y-m-d'));
		foreach ($train as &$item) {
			$item['lishi'] = strtotime($item['lishi']) - $x;
			$item['start'] = strtotime($dater . ' ' . $item['start']);
		}
		if (!$train) {
			return [];
		}
		
		$rules = DRules::getInstance()->getRule($from, $to, $dater);
		if (!$rules) {
			throw new Exception('无匹配规则');
		}
		
		$last = '';
		$count = 0;
		$exist = [];
		$passenger = [];
		
		foreach ($rules as $rule) {
			$pCount = count($rule['passenger']);
			if ($pCount + $count > self::PASSENGER_MAX) {
				continue;
			}
			
			// 与优先规则中任意旅客重叠，则跳过当前规则。
			$er = [];
			foreach ($rule['passenger'] as $x) {
				if (in_array($x[0], $exist)) {
					continue 2;
				}
				$er[] = $x[0];
			}
			
			foreach ($rule['seat'] as $seat) {
				if ($last && $seat[0] != $last && $seat[0] != 'X') { // 一个订单内禁止预订不同的车次。
					continue;
				}
				
				if ($last) {
					$no = $last;
				} elseif ($seat[0] == 'X') {
					$ts = []; // 非指明的车次需要进行发车时间范围限制。
					foreach ($train as $v) {
						if ($v['start'] >= $rule['early'] && $v['start'] <= $rule['latest']) {
							$ts[$v['no']] = $v;
						}
					}
					
					$no = $this->robot($ts, $seat[1], self::PASSENGER_MAX - $pCount);
					if (!$no) {
						continue;
					}
				} else {
					$no = $seat[0];
				}
				
				if (!isset($train[$no][$seat[1]])) { // 无此类型的席位。
					continue;
				} elseif ($train[$no][$seat[1]] < $pCount) { // 余票少于当前规则中的旅客数。
					continue;
				}
				
				$exist = array_merge($exist, $er);
				
				$last = $no;
				$count += $pCount;
				$train[$no][$seat[1]] -= $pCount;
				
				$passenger[$rule['rid']] = ['seat' => $seat[1], 'passenger' => $rule['passenger']];
				
				break;
			}
		}
		
		return $count ? ['no' => $last, 'rule' => $passenger] : [];
	}

	private function robot($train, $seat, $count) {
		$lishi = [];
		
		foreach ($train as $no => $item) {
			if (!isset($item[$seat]) || $item[$seat] < $count) {
				unset($train[$no]);
			} else {
				$lishi[$no] = $item['lishi'];
			}
		}
		
		if (count($train) <= 0) {
			return '';
		}
		
		// 删除历时较长的车次。
		asort($lishi);
		
		$first = reset($lishi);
		if ($first <= 28800) {
			$max = 5400; // 01:30
		} elseif ($first <= 43200) {
			$max = 9000; // 02:30
		} else {
			$max = 12600; // 03:30
		}
		
		// 遍历结束后，至少有一个车次被保留。
		foreach ($train as $no => $item) {
			if ($item['lishi'] - $first > $max) {
				unset($train[$no]);
			}
		}
		
		// =====================================================================
		$active = [];
		$x = count($train) > 4 ? 930 : 800;
		foreach ($train as $no => $item) {
			if (date('Gi', $item['start']) > $x) {
				$active[$no] = $item;
			}
		}
		
		if (count($active) > 0) {
			$train = &$active;
			
			if ($seat == '硬座' || $seat == '软座') {
				$limit = [930, 1500, 1730];
			} elseif ($seat == '硬卧' || $seat == '软卧') {
				$limit = [2000, 2300, 900];
			}
			
			$start = [];
			foreach ($train as $no => $item) {
				$x = date('Gi', $item['start']);
				if ($x > $limit[0] && $x < $limit[1]) {
					$start[$no] = $item;
				}
			}
			
			if (count($start) > 0) {
				$train = &$start;
				
				$arrive = [];
				foreach ($train as $no => $item) {
					$x = date('Gi', $item['start'] + $lishi[$no]);
					if ($x < $limit[2]) {
						$arrive[$no] = $item;
					}
				}
				
				if (count($arrive) > 0) {
					$train = &$arrive;
				}
			}
		}
		
		return array_rand($train);
	}

	/**
	 * 获得所有规则。
	 * 
	 * @return array
	 */
	public function getRules() {
		$list = DRules::getInstance()->getAll();
		
		$rules = [];
		
		foreach ($list as $rule) {
			isset($rules[$rule['from']][$rule['to']][$rule['dater']]) || $list[$rule['from']][$rule['to']][$rule['dater']] = [];
			
			$rules[$rule['from']][$rule['to']][$rule['dater']][$rule['rid']] = $rule;
		}
		
		return $rules;
	}

	public static function getInstance() {
		static $instance = NULL;
		
		if (!$instance) {
			$instance = new self();
		}
		
		return $instance;
	}
}